package com.song.elasticsearch.util;

import com.alibaba.fastjson.JSONObject;
import com.song.elasticsearch.es.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class ElasticsearchUtil<T> {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 简单查询
     * @param esIndexEnum
     * @param param
     * @param esQueryEnum
     * @param esTermEnum
     * @param sortField
     * @param order
     * @param t
     * @return
     * @throws IOException
     */
    public List<T> getSimpleSearchRes(EsIndexEnum esIndexEnum, Map<String, Object> param, EsQueryEnum esQueryEnum, EsTermEnum esTermEnum, String sortField, SortOrder order, Class<T> t) throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        param.forEach((k, v) -> {
            QueryBuilder queryBuilder = null;
            switch (esTermEnum){
                case TERM:
                    queryBuilder = QueryBuilders.termQuery(k, v);
                    break;
                case TERMS:
                    queryBuilder = QueryBuilders.termsQuery(k, v);
                    break;
                case MATCH_PARSE:
                    queryBuilder = QueryBuilders.matchPhraseQuery(k, v);
                    break;
            }
            switch (esQueryEnum){
                case MUST:
                    boolQueryBuilder.must(queryBuilder);
                    break;
                case MUSTNOT:
                    boolQueryBuilder.mustNot(queryBuilder);
                    break;
                case SHOULD:
                    boolQueryBuilder.should(queryBuilder);
                    break;
            }
        });
        if (StringUtils.isNotEmpty(sortField)) {
            builder.sort(sortField, order);
        }
        builder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(esIndexEnum.getIndex());
        searchRequest.source(builder);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        List<T> result = new ArrayList<>();
        for (SearchHit hit : hits) {
            T info = JSONObject.parseObject(hit.getSourceAsString(), t);
            result.add(info);
        }
        return result;
    }

    /**
     * 复杂查询
     * @param esIndexEnum
     * @param entities
     * @param sortFied
     * @param order
     * @param t
     * @return
     * @throws IOException
     */
    public List<T> getCompletRes(EsIndexEnum esIndexEnum, List<EsParamEntity> entities, String sortFied, SortOrder order, Class<T> t) throws IOException {
        SearchSourceBuilder builder = buildSearch(entities, sortFied, order);
        SearchRequest searchRequest = new SearchRequest(esIndexEnum.getIndex());
        searchRequest.source(builder);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        List<T> result = new ArrayList<>();
        for (SearchHit hit : hits) {
            T info = JSONObject.parseObject(hit.getSourceAsString(), t);
            result.add(info);
        }
        return result;
    }

    /**
     * 分页查询
     * @param indexEnum
     * @param param
     * @param sortFeild
     * @param order
     * @param t
     * @param pageNum
     * @param pageSize
     * @return
     * @throws Exception
     */
    public Page<T> pageList(EsIndexEnum indexEnum, List<EsParamEntity> param, String sortFeild, SortOrder order, Class<T> t, Integer pageNum, Integer pageSize) throws Exception {
        SearchSourceBuilder builder = buildSearch(param, sortFeild, order);
        if (ObjectUtils.isNotEmpty(pageNum)) {
            builder.from((pageNum - 1) * pageSize);
            builder.size(pageSize);
        }
        SearchRequest searchRequest = new SearchRequest(indexEnum.getIndex()).source(builder);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        List<T> records = new ArrayList<>();
        for (SearchHit hit : hits) {
            T t1 = JSONObject.parseObject(hit.getSourceAsString(), t);
            records.add(t1);
        }
        //封装分页结果
        Page<T> page = new Page<>();
        page.setPage(pageNum);
        page.setSize(pageSize);
        page.setRecords(records);
        long total = hits.getTotalHits().value;
        page.setTotal(total);
        int count = (int) total % pageSize;
        int pages = (int) total / pageSize;
        if (count > 0){
            pages += 1;
        }
        page.setPages(pages);
        return page;
    }

    private SearchSourceBuilder buildSearch(List<EsParamEntity> param, String sortField, SortOrder order) throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        if (CollectionUtils.isEmpty(param)) {
            builder.query(QueryBuilders.matchAllQuery());
        } else {
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            param.stream().forEach(es -> {
                QueryBuilder queryBuilder = null;
                String key = es.getColumnName();
                Object value = es.getValue();
                switch (es.getTermEnum()) {
                    case TERM:
                        queryBuilder = QueryBuilders.termQuery(key, value);
                        break;
                    case TERMS:
                        queryBuilder = QueryBuilders.termsQuery(es.getColumnName(), ((List<?>) value).toArray());
                        break;
                    case RANGE:
                        if (ObjectUtils.isNotEmpty(es.getFrom()) && ObjectUtils.isNotEmpty(es.getTo())) {
                            queryBuilder = QueryBuilders.rangeQuery(key).gte(es.getFrom()).lte(es.getTo());
                        }else if (ObjectUtils.isNotEmpty(es.getFrom()) && ObjectUtils.isEmpty(es.getTo())) {
                            queryBuilder = QueryBuilders.rangeQuery(key).gte(es.getFrom());
                        }else if (ObjectUtils.isEmpty(es.getFrom()) && ObjectUtils.isNotEmpty(es.getTo())) {
                            queryBuilder = QueryBuilders.rangeQuery(key).lte(es.getTo());
                        }
                        break;
                    case WILDCARD:
                        queryBuilder = QueryBuilders.wildcardQuery(key, "*" + value.toString() + "*");
                        break;
                    case EXISTS:
                        queryBuilder = QueryBuilders.existsQuery(key);
                        break;
                    case MATCH:
                        queryBuilder = QueryBuilders.matchQuery(key, value);
                        break;
                    case MATCH_PARSE:
                        queryBuilder = QueryBuilders.matchPhraseQuery(key, value);
                        break;
                }
                switch (es.getTypeEnum()) {
                    case MUST:
                        boolQueryBuilder.must(queryBuilder);
                        break;
                    case MUSTNOT:
                        boolQueryBuilder.mustNot(queryBuilder);
                        break;
                    case SHOULD:
                        boolQueryBuilder.should(queryBuilder);
                        break;
                    case FILTER:
                        boolQueryBuilder.filter(queryBuilder);
                        break;
                }
            });
            builder.query(boolQueryBuilder).from(0).size(10000);
        }
        if (StringUtils.isNotEmpty(sortField)) {
            builder.sort(sortField, order);
        }
        return builder;
    }

    public void group(EsIndexEnum esIndexEnum, String groupName, String groupField, int size) throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        AggregationBuilder aggregationBuilder = AggregationBuilders.terms(groupName).field(groupField).size(size);
        aggregationBuilder.subAggregation(AggregationBuilders.sum(""));
        aggregationBuilder.subAggregation(AggregationBuilders.topHits(""));
        builder.aggregation(aggregationBuilder);
        SearchRequest searchRequest = new SearchRequest(esIndexEnum.getIndex());
        searchRequest.source(builder);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Aggregations aggregations = response.getAggregations();
        ParsedStringTerms terms = aggregations.get(groupName);
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedTopHits parsedTopHits = bucketAggregations.get("");
            ParsedSum parsedSum = bucketAggregations.get("");
        }
    }

    public Object aggregation(EsIndexEnum esIndexEnum, EsAggEnum esAggEnum, String groupName, String groupField) throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        AggregationBuilder aggregationBuilder = null;
        switch (esAggEnum){
            case SUM:
                aggregationBuilder = AggregationBuilders.sum(groupName).field(groupField);
                break;
            case AVG:
                aggregationBuilder = AggregationBuilders.avg(groupName).field(groupField);
                break;
            case MAX:
                aggregationBuilder = AggregationBuilders.max(groupName).field(groupField);
                break;
            case MIN:
                aggregationBuilder = AggregationBuilders.min(groupName).field(groupField);
                break;
        }
        builder.aggregation(aggregationBuilder);
        SearchRequest searchRequest = new SearchRequest(esIndexEnum.getIndex());
        searchRequest.source(builder);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Aggregations aggregations = response.getAggregations();
        switch (esAggEnum){
            case SUM:
                ParsedSum parsedSum = aggregations.get(groupName);
                return parsedSum.getValue();
            case AVG:
                ParsedAvg parsedAvg = aggregations.get(groupName);
                return parsedAvg.getValue();
            case MAX:
                ParsedMax parsedMax = aggregations.get(groupName);
                return parsedMax.getValue();
            case MIN:
                ParsedMin parsedMin = aggregations.get(groupName);
                return parsedMin.getValue();
        }
        return null;
    }
}
